import moduleName from './name.js';
import $ from 'jquery';
import maNotificationTpl from './maNotificationTpl.html';
angular.module(moduleName).provider('$notification', notificationProvider);

notificationProvider.$inject = [];

function notificationProvider() {
  this.options = {
    delay: 5000,
    startTop: 20,
    startRight: 20,
    verticalSpacing: 10,
    horizontalSpacing: 10,
    positionX: 'right',
    positionY: 'top',
    replaceMessage: false,
    onClose: undefined,
    closeOnClick: true,
    maxCount: 0, // 0 - Infinite
    container: 'body',
    priority: 10
  };

  this.setOptions = function(options) {
    if (!angular.isObject(options)) throw new Error('Options should be an object!');
    this.options = angular.extend({}, this.options, options);
  };
  this.$get = ['$timeout', '$http', '$compile', '$rootScope', '$injector', '$sce', '$q',
    '$window',
    function($timeout, $http, $compile, $rootScope, $injector, $sce, $q, $window) {
      var options = this.options;
      var startTop = options.startTop;
      var startRight = options.startRight;
      var verticalSpacing = options.verticalSpacing;
      var horizontalSpacing = options.horizontalSpacing;
      var delay = options.delay;

      var messageElements = [];
      var isResizeBound = false;

      var notify = function(args, t) {
        var deferred = $q.defer();
        if (typeof args !== 'object' || args === null) {
          args = {
            message: args
          };
        }
        args.scope = args.scope ? args.scope : $rootScope;
        args.delay = !angular.isUndefined(args.delay) ? args.delay : delay;
        args.type = t || args.type || options.type || '';
        args.positionY = args.positionY ? args.positionY : options.positionY;
        args.positionX = args.positionX ? args.positionX : options.positionX;
        args.replaceMessage = args.replaceMessage ? args.replaceMessage : options.replaceMessage;
        args.onClose = args.onClose ? args.onClose : options.onClose;
        args.closeOnClick = (args.closeOnClick !== null && args.closeOnClick !== undefined) ?
          args.closeOnClick : options.closeOnClick;
        args.container = args.container ? args.container : options.container;
        args.priority = args.priority ? args.priority : options.priority;
        processNotificationTemplate(maNotificationTpl);

        function processNotificationTemplate(template) {
          var scope = args.scope.$new();
          scope.message = $sce.trustAsHtml(args.message);
          scope.title = $sce.trustAsHtml(args.title);
          scope.nClick = args.nClick;
          scope.t = args.type.substr(0, 1);
          scope.delay = args.delay;
          scope.onClose = args.onClose;

          var priorityCompareTop = function(a, b) {
            return a._priority - b._priority;
          };

          var priorityCompareBtm = function(a, b) {
            return b._priority - a._priority;
          };

          var reposite = function() {
            var j = 0;
            var k = 0;
            var lastTop = startTop;
            var lastRight = startRight;
            var lastPosition = [];
            if (args.positionY === 'top') {
              messageElements.sort(priorityCompareTop);
            } else if (args.positionY === 'bottom') {
              messageElements.sort(priorityCompareBtm);
            }

            for (var i = messageElements.length - 1; i >= 0; i--) {
              var element = messageElements[i];
              if (args.replaceMessage && i < messageElements.length - 1) {
                element.addClass('killed');
              } else {
                var elHeight = parseInt(element[0].offsetHeight, 10);
                var elWidth = parseInt(element[0].offsetWidth, 10);
                var position = lastPosition[element._positionY + element._positionX];
                if ((top + elHeight) > window.innerHeight) {
                  position = startTop;
                  k++;
                  j = 0;
                }
                var top;
                if (position) {
                  if (j === 0) {
                    top = position;
                    lastTop = position;
                  } else {
                    top = position + verticalSpacing;
                    lastTop = position + verticalSpacing;
                  }
                } else {
                  top = startTop; lastTop = startTop;
                }
                var right = lastRight + (k * (horizontalSpacing + elWidth));
                element.css(element._positionY, top + 'px');
                if (element._positionX == 'center') {
                  element.css('left', parseInt((window.innerWidth / 2) - (elWidth / 2), 10) + 'px');
                } else {
                  element.css(element._positionX, right + 'px');
                }
                lastPosition[element._positionY + element._positionX] = top + elHeight;

                if (options.maxCount > 0 && messageElements.length > options.maxCount && i ===
                  0) {
                  element.scope().kill(true);
                }
                j++;
              }
            }
          };

          var templateElement = $compile(template)(scope);
          templateElement._positionY = args.positionY;
          templateElement._positionX = args.positionX;
          templateElement._priority = args.priority;
          templateElement.addClass(args.type);
          var closeEvent = function(e) {
            e = e.originalEvent || e;
            if (e.type === 'click' || (e.propertyName === 'opacity' && e.elapsedTime >= 1)) {
              if (scope.onClose) {
                scope.$apply(scope.onClose(templateElement));
              }

              templateElement.remove();
              messageElements.splice(messageElements.indexOf(templateElement), 1);
              scope.$destroy();
              reposite();
            }
          };
          angular.element(templateElement).find('a').bind('click', function() {
            templateElement.remove();
            messageElements.splice(messageElements.indexOf(templateElement), 1);
            scope.$destroy();
            reposite();
          });
          if (args.closeOnClick) {
            templateElement.addClass('clickable');
            templateElement.bind('click', closeEvent);
          }
          templateElement.bind(
            'webkitTransitionEnd oTransitionEnd otransitionend transitionend msTransitionEnd',
            closeEvent);
          if (angular.isNumber(args.delay)) {
            $timeout(function() {
              templateElement.addClass('killed');
            }, args.delay);
          }
          setCssTransitions('none');

          angular.element(document.querySelector(args.container)).append(templateElement);
          var offset = -(parseInt(templateElement[0].offsetHeight, 10) + 50);
          templateElement.css(templateElement._positionY, offset + 'px');
          messageElements.push(templateElement);

          if (args.positionX == 'center') {
            var elWidth = parseInt(templateElement[0].offsetWidth, 10);
            templateElement.css('left', parseInt((window.innerWidth / 2) - (elWidth / 2), 10) +
              'px');
          }

          $timeout(function() {
            setCssTransitions('');
          });

          function setCssTransitions(value) {
            ['-webkit-transition', '-o-transition', 'transition'].forEach(function(prefix) {
              templateElement.css(prefix, value);
            });
          }

          scope._templateElement = templateElement;

          scope.kill = function(isHard) {
            if (isHard) {
              if (scope.onClose) {
                scope.$apply(scope.onClose(scope._templateElement));
              }

              messageElements.splice(messageElements.indexOf(scope._templateElement), 1);
              scope._templateElement.remove();
              scope.$destroy();
              $timeout(reposite);
            } else {
              scope._templateElement.addClass('killed');
            }
          };

          $timeout(reposite);

          if (!isResizeBound) {
            angular.element($window).bind('resize', function(e) {
              $timeout(reposite);
            });
            isResizeBound = true;
          }

          deferred.resolve(scope);
        }

        return deferred.promise;
      };

      notify.primary = function(args) {
        return this(args, 'primary');
      };
      notify.error = function(args) {
        return this(args, 'error');
      };
      notify.success = function(args) {
        return this(args, 'success');
      };
      notify.info = function(args) {
        return this(args, 'info');
      };
      notify.warning = function(args) {
        return this(args, 'warning');
      };

      notify.clearAll = function() {
        angular.forEach(messageElements, function(element) {
          element.addClass('killed');
        });
      };

      return notify;
    }
  ];
}
